/*
 * @title traitement.c
 * @brief Fonctions qui traitent les instructions.
 * @author Nathalie Kauv & Laura Martellotto
 *
 * @date 17 déc. 2011
 */

#include "traitement.h"

/**
 * Instruction LOAD
 * Destination REGISTRE
 * SOURCE IMMEDIAT
 * Exemple : LOAD R3, #3
 *          R3 = 3
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void loadRegImm(unsigned source, unsigned dest)
{
    reg.registres[dest] = source;
    printf("LOAD R%d, #%d\n", dest, source);
}

/**
 * Instruction LOAD
 * Destination REGISTRE
 * SOURCE DIRECT
 * Exemple : LOAD R3, [4]
 *          R3 = [3]
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void loadRegDir(unsigned source, unsigned dest)
{
    reg.registres[dest] = memoire[source].brut;
    printf("LOAD R%d, [%d]\n", dest, source);
}

/**
 * Instruction LOAD
 * Destination REGISTRE
 * SOURCE INDIRECT
 * Exemple : LOAD R3, [R4]
 *          R3 = [R4]
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void loadRegInd(unsigned source, unsigned dest)
{
    reg.registres[dest] = memoire[reg.registres[source]].brut;
    printf("LOAD R%d, [R%d]\n", dest, source);
}

/**
 * Instruction STORE
 * Destination INDIRECT
 * SOURCE REGISTRE
 * Exemple : STORE [R5], R3
 *          [R5] = R3
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void storeIndReg(unsigned source, unsigned dest)
{
    memoire[reg.registres[dest]%TAILLE_MEM].brut = reg.registres[source];
    printf("STORE [R%d], R%d\n", dest, source);
}


/**
 * Instruction STORE
 * Destination DIRECT
 * SOURCE IMMEDIAT
 * Exemple : STORE [5], #6
 *          [5] = 6
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void storeDirImm(unsigned source, unsigned dest)
{
    memoire[dest].brut = source;
    printf("STORE [%d], #%d\n", dest, source);
}

/**
 * Instruction STORE
 * Destination DIRECT
 * SOURCE REGISTRE
 * Exemple : STORE [3], R5
 *          [3] = R5
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void storeDirReg(unsigned source, unsigned dest)
{
    memoire[dest].brut = reg.registres[source];
    printf("STORE [%d], R%d\n", dest, source);
}

/**
 * Instruction STORE
 * Destination INDIRECT
 * SOURCE IMMEDIAT
 * Exemple : STORE [R5], #3
 *          [R5] = 3
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void storeIndImm(unsigned source, unsigned dest)
{
    memoire[reg.registres[dest]%TAILLE_MEM].brut = source;
    printf("STORE [R%d], #%d\n", dest, source);
}

/**
 * Instruction ADD
 * Destination REGISTRE
 * SOURCE REGISTRE
 * Exemple : ADD R4, R5
 *          R4 = R4 + R5
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void addRegReg(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] + reg.registres[source];
    reg.sr = reg.registres[dest];
    printf("ADD R%d, R%d\n", dest, source);
}

/**
 * Instruction ADD
 * Destination REGISTRE
 * SOURCE IMMEDIAT
 * Exemple : ADD R4, #3
 *          R4 = R4 + 3
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void addRegImm(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] + source;
    reg.sr = reg.registres[dest];
    printf("ADD R%d, #%d\n", dest, source);
}

/**
 * Instruction ADD
 * Destination REGISTRE
 * SOURCE DIRECT
 * Exemple : ADD R4, [6]
 *          R4 = R4 + [6]
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void addRegDir(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] + memoire[source].brut;
    reg.sr = reg.registres[dest];
    printf("ADD R%d, [%d]\n", dest, source);
}

/**
 * Instruction ADD
 * Destination REGISTRE
 * SOURCE INDIRECT
 * Exemple : ADD R4, [R6]
 *          R4 = R4 + [R6]
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void addRegInd(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] + memoire[reg.registres[source]].brut;
    reg.sr = reg.registres[dest];
    printf("ADD R%d, [R%d]\n", dest, source);
}

/**
 * Instruction SUB
 * Destination REGISTRE
 * SOURCE REGISTRE
 * Exemple : SUB R4, R5
 *          R4 = R4 - R5
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void subRegReg(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] - reg.registres[source];
    reg.sr = reg.registres[dest];
    printf("SUB R%d, R%d\n", dest, source);
}

/**
 * Instruction SUB
 * Destination REGISTRE
 * SOURCE IMMEDIAT
 * Exemple : SUB R4, #3
 *          R4 = R4 - 3
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void subRegImm(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] - source;
    reg.sr = reg.registres[dest];
    printf("SUB R%d, #%d\n", dest, source);
}

/**
 * Instruction SUB
 * Destination REGISTRE
 * SOURCE DIRECT
 * Exemple : SUB R4, [6]
 *          R4 = R4 - [6]
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void subRegDir(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] - memoire[source].brut;
    reg.sr = reg.registres[dest];
    printf("SUB R%d, [%d]\n", dest, source);
}

/**
 * Instruction SUB
 * Destination REGISTRE
 * SOURCE INDIRECT
 * Exemple : SUB R4, [R6]
 *          R4 = R4 - [R6]
 * @param source Source de l'instruction
 * @param dest Destination de l'instruction
 */
void subRegInd(unsigned source, unsigned dest)
{
    reg.registres[dest] = reg.registres[dest] - memoire[reg.registres[source]].brut;
    reg.sr = reg.registres[dest];
    printf("SUB R%d, [R%d]\n", dest, source);
}

/**
 * Instruction JMP
 * SOURCE IMMEDIAT
 * Exemple : JMP #5
 *          PC <- 5
 * @param source Source de l'instruction
 */
void jmpImm(unsigned source)
{
    reg.pc = source;
    printf("JMP #%d\n", source);
}

/**
 * Instruction JEQ
 * SOURCE IMMEDIAT
 * Exemple : JEQ #5
 *          PC <- 5     si  SR = 0
 * @param source Source de l'instruction
 */
void jeqImm(unsigned source)
{
    if (reg.sr == 0)
        reg.pc = source;
    else
        reg.pc = reg.pc + 2;
    printf("JEQ #%d\n", source);
}

/**
 * Instruction CALL
 * SOURCE IMMEDIAT
 * Exemple : CALL #5
 *          [SP] <- PC
 *          SP <- SP - 1
 *          PC <- 5
 * @param source Source de l'instruction
 */
void callImm(unsigned source)
{
    memoire[reg.sp].brut = reg.pc  + 4;
    reg.sp = reg.sp - 1;
    reg.pc = source;
    printf("CALL #%d\n", source);

}

/**
 * Instruction RET
 * Exemple : RET
 *          SP = SP + 1
 *          PC <- [SP]
 */
void ret()
{
    if (reg.sp < TAILLE_MEM - 1)
    {
        reg.sp = reg.sp + 1;
        reg.pc = memoire[reg.sp].brut;
        printf("RET\n");
    }

}

/**
 * Instruction PUSH
 * SOURCE REGISTRE
 * Exemple : push R5
 *          [SP] <- R5
 *          SP = SP - 1
 * @param source Source de l'instruction
 */
void pushReg(unsigned source)
{
    memoire[reg.sp].brut = reg.registres[source];
    reg.sp = reg.sp - 1;
    printf("PUSH R%d\n", source);
}

/**
 * Instruction PUSH
 * SOURCE IMMEDIAT
 * Exemple : push #5
 *          [SP] <- 5
 *          SP = SP - 1
 * @param source Source de l'instruction
 */
void pushImm(unsigned source)
{
    memoire[reg.sp].brut = source;
    reg.sp = reg.sp - 1;
    printf("PUSH #%d\n", source);
}

/**
 * Instruction PUSH
 * SOURCE DIRECT
 * Exemple : push [5]
 *          [SP] <- [5]
 *          SP = SP - 1
 * @param source Source de l'instruction
 */
void pushDir(unsigned source)
{
    memoire[reg.sp] = memoire[source];
    reg.sp = reg.sp - 1;
    printf("PUSH [%d]\n", source);
}

/**
 * Instruction PUSH
 * SOURCE INDIRECT
 * Exemple : push [R5]
 *          [SP] <- [R5]
 *          SP = SP - 1
 * @param source Source de l'instruction
 */
void pushInd(unsigned source)
{
    memoire[reg.sp] = memoire[reg.registres[source]];
    reg.sp = reg.sp - 1;
    printf("PUSH [R%d]\n", source);
}

/**
 * Instruction POP
 * DESTINATION REGISTRE
 * Exemple : pop R5
 *          SP = SP + 1
 *          R5 <- [SP]
 * @param dest Destination de l'instruction
 */
void popReg(unsigned dest)
{
    reg.sp = reg.sp + 1;
    reg.registres[dest] = memoire[reg.sp].brut;
    printf("POP R%d\n", dest);
}

/**
 * Instruction POP
 * DESTINATION DIRECT
 * Exemple : pop [5]
 *          SP = SP + 1
 *          [5] <- [SP]
 * @param dest Destination de l'instruction
 */
void popDir(unsigned dest)
{
    reg.sp = reg.sp + 1;
    memoire[dest] = memoire[reg.sp];
    printf("POP [%d]\n", dest);
}


/**
 * Instruction POP
 * DESTINATION INDIRECT
 * Exemple : pop [R5]
 *          SP = SP + 1
 *          [R5] <- [SP]
 * @param dest Destination de l'instruction
 */
void popInd(unsigned dest)
{
    reg.sp = reg.sp + 1;
    memoire[reg.registres[dest]] = memoire[reg.sp];
    printf("POP [R%d]\n", dest);
}


/**
 * Instruction HALT
 * Exemple : halt
 *
 */
void halt()
{
    printf("HALT\n");
}




/**
 * Affiche les registres et leur contenu respectif
 */
void afficherRegistres()
{
    printf("\n- REGISTRES -\n");

    int i;
    for (i = 0; i < 8; ++i)
    {
        if (i == 0)
            printf("R%d : %d \t\t PC : %d\n", i,reg.registres[i], reg.pc);
        else if (i == 1)
            printf("R%d : %d \t\t SP : %d\n", i,reg.registres[i], reg.sp);
        else if (i == 2)
            printf("R%d : %d \t\t SR : %d\n", i,reg.registres[i], reg.sr);
        else
            printf("R%d : %d \n", i,reg.registres[i]);
    }
}

/**
 * Affiche la mémoire centrale
 */
void afficherMemoire()
{
    int i;
    printf("\n- MEMOIRE -\n");
    for (i = 0; i < 10; i++)
    {
        printf("[%d] : %d \t[%d] : %d\t[%d] : %d\t[%d] : %d\t[%d] : %d\t[%d] : %d\n",
                i, memoire[i],
                i+10, memoire[i+10],
                i+20, memoire[i+20],
                i+30, memoire[i+30],
                i+40, memoire[i+40],
                i+50, memoire[i+50]
        );
    }
}

/**
 * Fonction d'appel aux fonctions de traitement LOAD désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterLoad (mot codInst)
{
    unsigned secondMot;
    switch (codInst.codage.mode) {
    case REGIMM :
        secondMot = memoire[reg.pc + 1].brut;
        loadRegImm(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case REGDIR :
        secondMot = memoire[reg.pc + 1].brut;
        loadRegDir(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case REGIND :
        loadRegInd(codInst.codage.source, codInst.codage.dest);
        break;
    }
}

/**
 * Fonction d'appel aux fonctions de traitement STORE désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterStore (mot codInst)
{
    unsigned secondMot;
    unsigned troisiemeMot;

    switch (codInst.codage.mode) {
    case DIRIMM :
        secondMot = memoire[reg.pc + 1].brut;
        troisiemeMot = memoire[reg.pc + 2].brut;
        storeDirImm(secondMot, troisiemeMot);
        reg.pc++;
        reg.pc++;
        break;
    case DIRREG :
        secondMot = memoire[reg.pc + 1].brut;
        storeDirReg(codInst.codage.source, secondMot);
        reg.pc++;
        break;
    case INDIMM :
        secondMot = memoire[reg.pc + 1].brut;
        storeIndImm(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case INDREG :
        storeIndReg(codInst.codage.source, codInst.codage.dest);
        break;
    }
}

/**
 * Fonction d'appel aux fonctions de traitement ADD désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterAdd (mot codInst)
{
    unsigned secondMot;
    switch (codInst.codage.mode) {
    case REGREG :
        addRegReg(codInst.codage.source, codInst.codage.dest);
        break;
    case REGIMM :
        secondMot = memoire[reg.pc + 1].brut;
        addRegImm(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case REGDIR :
        secondMot = memoire[reg.pc + 1].brut;
        addRegDir(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case REGIND :
        addRegInd(codInst.codage.source, codInst.codage.dest);
        break;
    }
}

/**
 * Fonction d'appel aux fonctions de traitement SUB désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterSub (mot codInst)
{
    unsigned secondMot;
    switch (codInst.codage.mode) {
    case REGREG :
        subRegReg(codInst.codage.source, codInst.codage.dest);
        break;
    case REGIMM :
        secondMot = memoire[reg.pc + 1].brut;
        subRegImm(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case REGDIR :
        secondMot = memoire[reg.pc + 1].brut;
        subRegDir(secondMot, codInst.codage.dest);
        reg.pc++;
        break;
    case REGIND :
        subRegInd(codInst.codage.source, codInst.codage.dest);
        break;
    }
}

/**
 * Fonction d'appel aux fonctions de traitement JMP désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterJmp (mot codInst)
{
    unsigned secondMot;
    secondMot = memoire[reg.pc + 1].brut;
    jmpImm(secondMot);
}

/**
 * Fonction d'appel aux fonctions de traitement JEQ désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterJeq (mot codInst)
{
    unsigned secondMot;
    secondMot = memoire[reg.pc + 1].brut;
    jeqImm(secondMot);
}

/**
 * Fonction d'appel aux fonctions de traitement CALL désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterCall (mot codInst)
{
    unsigned secondMot;
    secondMot = memoire[reg.pc + 1].brut;
    callImm(secondMot);
}

/**
 * Fonction d'appel aux fonctions de traitement PUSH désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterPush (mot codInst)
{
    unsigned secondMot;
    switch (codInst.codage.mode) {
    case REGREG :
    case INDREG :
    case DIRREG :
        pushReg(codInst.codage.source);
        break;
    case DIRIMM :
    case REGIMM :
    case INDIMM :
        secondMot = memoire[reg.pc + 1].brut;
        pushImm(secondMot);
        reg.pc++;
        break;
    case REGDIR :
        secondMot = memoire[reg.pc + 1].brut;
        pushDir(secondMot);
        reg.pc++;
        break;
    case REGIND :
        pushInd(codInst.codage.source);
        break;
    }
}

/**
 * Fonction d'appel aux fonctions de traitement POP désirées en fonction du mode d'adressage
 * @param codInst La structure mot qui est traitée
 */
void traiterPop (mot codInst)
{
    if (reg.sp < TAILLE_MEM - 1) {
        unsigned secondMot;
        switch (codInst.codage.mode) {
        case REGREG :
        case REGIND :
        case REGDIR :
        case REGIMM :
            popReg(codInst.codage.dest);
            break;
        case DIRIMM :
        case DIRREG :
            secondMot = memoire[reg.pc + 1].brut;
            popDir(secondMot);
            reg.pc++;
            break;
        case INDIMM :
        case INDREG :
            popInd(codInst.codage.dest);
            break;

        }
    }
}
